﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.IO;
using System.Management;
using System.Text.RegularExpressions;
using System.Web;
using System.Xml;
using Microsoft.Win32;
using System.Web.UI;
using System.ComponentModel;
using System.Net;
using System.Threading;
using Ionic.Utils.Zip;
using System.Resources;

namespace raktajino
{
    #region Products
    /// <summary>
    /// Products - Container for all available Packages
    /// </summary>
    public class Products
    {
        public List<Package> Packages = new List<Package>();
        XmlDocument XmlInstallRequirements = new XmlDocument();


        public Products(XmlDocument HostingOptions)
        {
            // fill the InstallRequirements doc
            string urlInstallationRequirements = ConfigurationSettings.AppSettings["InstallationRequirementsURL"];
            if (urlInstallationRequirements == string.Empty)
            {
                string ExMsg = string.Format("No installationRequirements url found in config file!");
                System.Windows.Forms.MessageBox.Show(ExMsg, "Error");
            }

            try
            {
                XmlInstallRequirements.Load(urlInstallationRequirements);
            }
            catch (SystemException ex)
            {
                string ExMsg = string.Format("A problem occurred when loading the installationRequirements ({0}):\n{1}", urlInstallationRequirements, ex.Message);
                System.Windows.Forms.MessageBox.Show(ExMsg, "Error");
            }

            XmlNodeList xmllistproducts = HostingOptions.DocumentElement.SelectNodes("//Product");

            for (int i = 0; i < xmllistproducts.Count; i++)
            {
                XmlNode xmlProduct = xmllistproducts[i].FirstChild;
                string PackageName = xmlProduct.InnerText;
                Package package = new Package(PackageName, HostingOptions, XmlInstallRequirements);
                this.Packages.Add(package);
            }
        }
    }
    #endregion

    /// <summary>
    /// Contains all actions required to install - including Installationcommands (
    /// </summary>
    public class InstallAction
    {
        public XmlNode XmlAction;
        public InstallationCommand Command;
        public bool done;
        public int ExitCode;
        public string ExitInfoString;

        public InstallAction()
        {
            this.ExitCode = -1000;
            this.done = false;
            this.XmlAction = null;
            this.Command = null;
            this.ExitInfoString = string.Empty;
        }
    }

    /// <summary>
    /// Package - Contains all information necessary to run installation process
    /// </summary>
    public class Package
    {
        public string Name;
        public string Displaytext;
        public string EulaUrl;
        public string DescriptionUrl;
        public string SelfCheck;
        public List<string> CategoryList = new List<string>();
        public XmlNode ProductXmlNode;
        public string InstallationUrl;
        public UserInput UserInput;
        public Uri DownloadUrl;
        object installationOK;

        public InstallationRequirements InstallationRequirements;
        private XmlDocument XmlInstallRequirements = null;
        public List<InstallAction> InstallationActions = new List<InstallAction>();

        public delegate void PackageInstallationCompletedEventHandler(object sender, AsyncCompletedEventArgs e);
        public event PackageInstallationCompletedEventHandler PackageInstallationCompletedEvent = null;

        public delegate void PackageInstallationProgressChangedEventHandler(object sender, ProgressChangedEventArgs e);
        public event PackageInstallationProgressChangedEventHandler PackageInstallationProgressChangedEvent = null;

        BackgroundWorker installer = new BackgroundWorker();
        System.Globalization.CultureInfo ci = System.Windows.Forms.Application.CurrentCulture;
        ResourceManager LocRM = new ResourceManager("raktajino.WinformStrings", typeof(RAKTAJINO).Assembly);

        public Package(string PackageName, XmlDocument HostingOptions, XmlDocument XmlInstallRequirements)
        {
            this.Name = PackageName;
            this.XmlInstallRequirements = XmlInstallRequirements;
            string Productsearchpath = string.Format("//Product[Title='{0}']", this.Name);

            XmlNode XmlProduct = HostingOptions.SelectSingleNode(string.Format("{0}", Productsearchpath));
            this.ProductXmlNode = XmlProduct;

            Uri uri = null;
            //check the InstallURL if it is a http address if so then set CommandURI to currentdirctory\downloads.
            try
            {
                uri = new Uri(this.ProductXmlNode.SelectSingleNode("InstallURL").InnerText);
                if (uri.IsFile)
                {
                    this.InstallationUrl = uri.ToString();
                }
                else
                {
                    if (!uri.IsAbsoluteUri)
                    {
                        //throw new InvalidInputException(string.Format("InstallURL '{0}' is not an absolute uri \n I need e.g. 'http://myserver/test.msi'", uri.ToString()));
                        this.InstallationUrl = string.Empty;
                    }
                    else
                    {
                        //uri is valid hyperlink - comanduri is set to the downloaddirectory 
                        this.DownloadUrl = uri;
                        this.InstallationUrl = System.Windows.Forms.Application.LocalUserAppDataPath + "\\downloads";
                    }
                }
            }
            catch (SystemException)
            {
                //handle case if e.g. <InstallURL> is not existing if you don't need to download anything.
                this.InstallationUrl = string.Empty;
            }


            try
            {
                this.Displaytext = string.Format("{0}<hr/>", XmlProduct.SelectSingleNode(string.Format("{0}/Description", Productsearchpath)).InnerText);
            }
            catch
            {
                this.Displaytext = string.Empty;
            }

            try
            {
                this.DescriptionUrl = string.Format("{0}", XmlProduct.SelectSingleNode(string.Format("{0}/DescriptionUrl", Productsearchpath)).InnerText);
            }
            catch
            {
                this.DescriptionUrl = string.Empty;
            }

            XmlNodeList xmllistcategories = XmlProduct.SelectNodes(string.Format("{0}/Categories/Category", Productsearchpath));
            for (int ii = 0; ii < xmllistcategories.Count; ii++)
            {
                string categoryEntry = xmllistcategories[ii].InnerText;
                if (!CategoryList.Contains(categoryEntry))
                {
                    CategoryList.Add(categoryEntry);
                }
            }

            try
            {
                this.EulaUrl = string.Format("{0}", XmlProduct.SelectSingleNode(string.Format("{0}/EulaUrl", Productsearchpath)).InnerText);
            }
            catch
            {
                this.EulaUrl = string.Empty;
            }

            try
            {
                this.SelfCheck = string.Format("{0}", XmlProduct.SelectSingleNode(string.Format("{0}/Requirements/SelfCheck/Requirement", Productsearchpath)).InnerText);
            }
            catch
            {
                this.SelfCheck = string.Empty;
            }


            this.UserInput = new UserInput(XmlProduct.SelectNodes("UserInputs"));

            GetInstallationActions();

        }

        public void GetInstallationActions()
        {
            XmlNodeList xmlActions = this.ProductXmlNode.SelectNodes("Action");

            foreach (XmlNode singleAction in xmlActions)
            {
                InstallAction InstallationAction = new InstallAction();
                InstallationAction.XmlAction = singleAction;
                this.InstallationActions.Add(InstallationAction);
            }
        }

        public bool CheckRequirements()
        {
            this.InstallationRequirements = new InstallationRequirements(this.ProductXmlNode, this.XmlInstallRequirements);
            return this.InstallationRequirements.isValidInstallationRequirements;
        }

        public void GetInstallationCommand()
        {
            foreach (InstallAction singleInstallAction in this.InstallationActions)
            {
                InstallationCommand command = new InstallationCommand(this);
                command.CreateCommand(singleInstallAction.XmlAction, this.UserInput);
                singleInstallAction.Command = command;
            }

        }

        /// <summary>
        /// Installs a package reports progress and end through events and throws exception in case package cannot install due to download errors or missing prerequisite packages.
        /// </summary>
        public void InstallPackage()
        {
            string output = string.Empty;
            //can the package install?
            HelperFunctions.CachedCheckInstallationRequirements.Clear();
            this.CheckRequirements();
            if (!this.InstallationRequirements.InstallCheckSuccessfull)
            {
                if (this.InstallationRequirements.ProductAlreadyInstalled)
                {
                    return;
                }
                foreach (string entry in this.InstallationRequirements.CheckResults)
                {
                    output += entry + "\n";
                }
                throw new Exception(output);
            }

            installationOK = true;
            //Do download if needed
            if (!string.IsNullOrEmpty(this.InstallationUrl))
            {
                WebClient download = new WebClient();
                download.DownloadFileCompleted += new AsyncCompletedEventHandler(download_DownloadFileCompleted);
                download.DownloadProgressChanged += new DownloadProgressChangedEventHandler(download_DownloadProgressChanged);
                string fileName = Path.Combine(this.InstallationUrl, Path.GetFileName(this.DownloadUrl.LocalPath));
                if (!Directory.Exists(Path.GetDirectoryName(fileName)))
                {
                    Directory.CreateDirectory(Path.GetDirectoryName(fileName));
                }
                download.DownloadFileAsync(this.DownloadUrl, fileName, this.DownloadUrl.ToString());
                while (download.IsBusy)
                {
                    Thread.Sleep(300);
                }
                download.DownloadFileCompleted -= new AsyncCompletedEventHandler(download_DownloadFileCompleted);
                download.DownloadProgressChanged -= new DownloadProgressChangedEventHandler(download_DownloadProgressChanged);
                download.Dispose();
                //file download successful?
                if (!installationOK.Equals(true))
                {
                    output = LocRM.GetString("A_download_error_has_occurred", ci);
                    throw new Exception(output);
                }
            }

            //start installation
            installer.WorkerReportsProgress = true;
            installer.DoWork += new DoWorkEventHandler(installer_DoWork);
            installer.RunWorkerCompleted += new RunWorkerCompletedEventHandler(installer_RunWorkerCompleted);
            installer.ProgressChanged += new ProgressChangedEventHandler(installer_ProgressChanged);

            installer.RunWorkerAsync();
            while (installer.IsBusy)
            {
                Thread.Sleep(300);
            }
            installer.DoWork -= new DoWorkEventHandler(installer_DoWork);
            installer.RunWorkerCompleted -= new RunWorkerCompletedEventHandler(installer_RunWorkerCompleted);
            installer.ProgressChanged -= new ProgressChangedEventHandler(installer_ProgressChanged);
            installer.Dispose();
        }

        void installer_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            PackageInstallationProgressChangedEvent(sender, new ProgressChangedEventArgs(e.ProgressPercentage, e.UserState));
        }

        void installer_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            PackageInstallationCompletedEvent(sender, new AsyncCompletedEventArgs(e.Error, e.Cancelled, e.Result));
        }

        void installer_DoWork(object sender, DoWorkEventArgs e)
        {
            this.GetInstallationCommand();
            int counter = 0;

            foreach (InstallAction installAction in this.InstallationActions)
            {
                object response = string.Empty;
                int progress;
                counter++;
                try
                {
                    progress = (100 * counter) / this.InstallationActions.Count;
                }
                catch (DivideByZeroException)
                {
                    progress = 100;
                }
                ProcessStartInfo processStartInfo = new ProcessStartInfo(installAction.Command.Command);
                processStartInfo.Arguments = installAction.Command.Arguments;
                PackageInstallationProgressChangedEvent(sender, new ProgressChangedEventArgs(progress, installAction.Command.Command));
                #region UNZIP Action
                if (installAction.Command.Command.Contains("UNZIP"))
                {
                    try
                    {
                        string[] parameters = installAction.Command.Arguments.Split('@');
                        if (parameters.Length < 3)
                        {
                            lock (installationOK)
                            {
                                installationOK = false;
                            }
                            response = string.Format("{0},{1},{2},{3},{4}", LocRM.GetString("Unzipping", ci), this.Name, installAction.Command.Command, installAction.Command.Arguments, LocRM.GetString("Too_few_UNZIP_parameters_at", ci));
                            installer_RunWorkerCompleted(sender, new RunWorkerCompletedEventArgs(response, null, false));
                            return; //abort installation thread
                        }
                        string zipfilename = parameters[1].ToString();
                        string outputfolder = parameters[2].ToString();
                        //localization
                        if (!File.Exists(zipfilename))
                        {
                            lock (installationOK)
                            {
                                installationOK = false;
                            }
                            response = string.Format("{0},{1},{2},{3},{4}", LocRM.GetString("Unzipping", ci), this.Name, zipfilename, string.Empty, LocRM.GetString("Zip_file_cannot_be_found", ci));
                            installer_RunWorkerCompleted(sender, new RunWorkerCompletedEventArgs(response, null, false));
                            return; //abort installation thread
                        }
                        ZipFile zip = new ZipFile(zipfilename);
                        System.IO.FileInfo fi = new System.IO.FileInfo(zipfilename);
                        decimal size = (decimal)fi.Length;
                        int unzippedsize = 0;
                        response = string.Format(LocRM.GetString("Unzipping", ci) + ": {0}", zipfilename);
                        foreach (ZipEntry entry in zip)
                        {
                            // unzip and inform the user what we are currently doing
                            int percentageCompleted = (int)((decimal)(100 / size) * unzippedsize);
                            installer.ReportProgress(percentageCompleted, response);
                            entry.Extract(outputfolder);
                            unzippedsize += entry.CompressedSize;
                        }
                        installAction.ExitCode = 0;
                        zip.Dispose();
                        response = LocRM.GetString("UNZIP_successful", ci);
                        installer.ReportProgress(100, response);
                    }
                    catch (SystemException se)
                    {
                        lock (installationOK)
                        {
                            installationOK = false;
                        }
                        response = string.Format("{0},{1},{2},{3},{4}", LocRM.GetString("Error_during_unzip", ci), this.Name, installAction.Command.Command, installAction.Command.Arguments, se.Message);
                        installer_RunWorkerCompleted(sender, new RunWorkerCompletedEventArgs(response, null, false));
                        return; //abort installation thread
                    }
                    finally
                    {
                        installAction.done = true;
                    }
                }
                #endregion

                #region Process Action
                if (installAction.Command.Command.Contains("cmd.exe"))
                {
                    processStartInfo.UseShellExecute = true;
                    processStartInfo.WindowStyle = ProcessWindowStyle.Hidden;   //cosmetics
                    processStartInfo.RedirectStandardError = false;
                    processStartInfo.RedirectStandardOutput = false;
                    processStartInfo.WorkingDirectory = Environment.GetFolderPath(Environment.SpecialFolder.System);
                }
                else
                {
                    processStartInfo.UseShellExecute = false;
                    processStartInfo.RedirectStandardError = true;
                    processStartInfo.RedirectStandardOutput = true;
                }
                Process InstallationProcess = new Process();
                InstallationProcess.StartInfo = processStartInfo;

                try
                {
                    response = string.Format("{0},{1},{2},{3},{4}", LocRM.GetString("Installing", ci), this.Name, installAction.Command.Command, installAction.Command.Arguments, string.Empty);
                    DateTime startTime = DateTime.Now;
                    InstallationProcess.Start();
                    while (!InstallationProcess.HasExited)
                    {
                        //inform the user what we are currently doing
                        System.Threading.Thread.Sleep(1000);
                        installer.ReportProgress(0, response + (DateTime.Now - startTime).Seconds.ToString());
                    }

                    //do stuff after installation has exited
                    installAction.ExitCode = InstallationProcess.ExitCode;
                    response = string.Format("{0},{1},{2},{3},{4}", LocRM.GetString("Installing", ci), this.Name, installAction.Command.Command, installAction.Command.Arguments, LocRM.GetString("Exit_Code", ci) + ":" + InstallationProcess.ExitCode);
                    installer.ReportProgress(0, response);
                }
                catch (SystemException se)
                {
                    installAction.ExitInfoString = se.Message;
                    lock (installationOK)
                    {
                        installationOK = false;
                    }
                    response = string.Format("{0},{1},{2},{3},{4}", LocRM.GetString("Error", ci), this.Name, installAction.Command.Command, installAction.Command.Arguments, se.Message);
                    installer_RunWorkerCompleted(sender, new RunWorkerCompletedEventArgs(response, null, false));
                    return; //abort installation thread
                }
                finally
                {
                    installAction.done = true;
                }
                response = string.Format("{0},{1},{2},{3},{4}", LocRM.GetString("TabPage1Text", ci), this.Name, installAction.Command.Command, installAction.Command.Arguments, string.Empty);
                installer_RunWorkerCompleted(sender, new RunWorkerCompletedEventArgs(response, null, false));

                #endregion
            }
        }

        void download_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
        {
            if (e.Cancelled == true || e.Error != null)
            {
                lock (installationOK)
                {
                    installationOK = false;
                }
                object response = string.Format("{0},{1},{2},{3},{4}", LocRM.GetString("Downloading", ci), this.Name, e.UserState, string.Empty, e.Error.Message);
                //installer_RunWorkerCompleted(sender, new RunWorkerCompletedEventArgs(response, null, false));
                PackageInstallationCompletedEvent(sender, new AsyncCompletedEventArgs(e.Error, e.Cancelled, response));
            }
            else
            {
                object response = string.Format("{0},{1},{2},{3},{4}", LocRM.GetString("Downloading", ci), this.Name, string.Empty, string.Empty, string.Empty);
                PackageInstallationProgressChangedEvent(sender, new ProgressChangedEventArgs(100, response));
            }

        }

        void download_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
        {
            object response = string.Format("{0},{1},{2},{3},{4}", LocRM.GetString("Downloading", ci), this.Name, e.UserState, string.Empty, e.ProgressPercentage.ToString());
            PackageInstallationProgressChangedEvent(sender, new ProgressChangedEventArgs(e.ProgressPercentage, response));
        }
    }

    /// <summary>
    /// InstallatioinCommand - builds the command that is executed to install the Package
    /// </summary>
    public class InstallationCommand
    {
        public string Command;
        public string Arguments;
        public Package Package;

        public InstallationCommand(Package Package)
        {
            this.Command = string.Empty;
            this.Arguments = string.Empty;
            this.Package = Package;
        }

        public void CreateCommand(XmlNode XmlAction, UserInput UserInput)
        {
            //string CommandURI = this.Package.InstallationUrl;
            string CommandType = XmlAction.SelectSingleNode("Type").InnerText;
            string filename = string.Empty;

            XmlNodeList XmlParameters = XmlAction.SelectNodes("Parameters/*");

            switch (CommandType.ToUpper())
            {
                case "EXE":
                    Command = Path.GetFullPath(HelperFunctions.ParseSpecialStrings(XmlAction.SelectSingleNode("Command").InnerText));
                    //filename = this.Command;

                    if (XmlParameters.Count > 0)
                    {
                        this.Arguments += string.Format("{0}", MakeParametersToArguments(UserInput, XmlParameters, " "));
                    }
                    this.Arguments = HelperFunctions.ParseSpecialStrings(this.Arguments);
                    break;

                case "CMD":
                    Command = string.Format(@"{0}\cmd.exe", Environment.GetFolderPath(Environment.SpecialFolder.System));
                    this.Arguments = string.Format(" /C {0}", XmlAction.SelectSingleNode("Command").InnerText);

                    if (XmlParameters.Count > 0)
                    {
                        this.Arguments += string.Format("{0}", MakeParametersToArguments(UserInput, XmlParameters, " "));
                    }
                    this.Arguments = HelperFunctions.ParseSpecialStrings(this.Arguments);
                    break;

                case "MSI":
                    filename = Path.GetFullPath(HelperFunctions.ParseSpecialStrings(XmlAction.SelectSingleNode("Command").InnerText));
                    Command = string.Format(@"{0}\msiexec.exe", Environment.GetFolderPath(Environment.SpecialFolder.System));
                    this.Arguments += string.Format(" /Package \"{0}\" ", filename);

                    if (XmlParameters.Count > 0)
                    {
                        this.Arguments += string.Format("{0}", MakeParametersToArguments(UserInput, XmlParameters, " "));
                    }
                    this.Arguments = HelperFunctions.ParseSpecialStrings(this.Arguments);
                    break;

                case "UNZIP":
                    //unzip is encapsulated in the code (you will find this logic in the winforms logic) - it is not an external proc that is to be launched
                    Command = "UNZIP";
                    if (XmlParameters.Count > 0)
                    {
                        this.Arguments += string.Format("{0}", MakeParametersToArguments(UserInput, XmlParameters, "@"));
                    }
                    this.Arguments = HelperFunctions.ParseSpecialStrings(this.Arguments);
                    break;


                default:
                    throw new InvalidInputException(string.Format("Command Type '{0}' not supported", CommandType));
                //break;
            }

        }

        #region Parameters
        private string MakeParametersToArguments(UserInput UserInput, XmlNodeList XmlParameters, string ArgumentDelimeterString)
        {
            string fullArgumentValue = string.Empty;
            if (string.IsNullOrEmpty(ArgumentDelimeterString))
            {
                ArgumentDelimeterString = " ";
            }

            foreach (XmlNode paramNode in XmlParameters)
            {
                //this is the hardcoded parameter with no userinput - we can see that as there are no attributes in the xml file
                if (paramNode.Attributes.Count == 0)
                {
                    fullArgumentValue += string.Format("{0}{1}", ArgumentDelimeterString, paramNode.InnerText);
                    continue;
                }
                //The others must be UserInput parameters
                foreach (UserInputParameter userInputParm in UserInput.Parameters)
                {
                    if (paramNode.Attributes["src"].Value == userInputParm.Name)
                    {
                        switch (userInputParm.InputType)
                        {
                            case UserInputType.uiTypeBool:
                                bool result = (bool)userInputParm.Result;
                                switch (result)
                                {
                                    case true:
                                        fullArgumentValue += string.Format("{0}{1}", ArgumentDelimeterString, paramNode.Attributes["valuetrue"].Value);
                                        break;
                                    case false:
                                        fullArgumentValue += string.Format("{0}{1}", ArgumentDelimeterString, paramNode.Attributes["valuefalse"].Value);
                                        break;
                                    default:
                                        break;
                                }
                                break;
                            case UserInputType.uiTypeString:
                                string XmlParameterValue = paramNode.Attributes["value"].Value;
                                fullArgumentValue += string.Format("{0}{1}", ArgumentDelimeterString, XmlParameterValue.Replace("%value", (string)userInputParm.Result));
                                break;
                            default:
                                break;
                        }
                    }
                }
            }
            return fullArgumentValue;
        }

        #endregion

    }

    /// <summary>
    /// Categories contains a dictionary object in the format <Category> --- <Product><Title>,<Product><Title>,....
    /// </summary>
    public class Categories
    {
        public Dictionary<string, List<string>> categoryDictionary = new Dictionary<string, List<string>>();

        public Categories(XmlDocument HostingOptions)
        {
            XmlNodeList xmllistproducts = HostingOptions.DocumentElement.SelectNodes("//Product");
            List<string> allProductsList = new List<string>();

            for (int i = 0; i < xmllistproducts.Count; i++)
            {
                XmlNode Product = xmllistproducts[i].FirstChild;
                String PackageName = Product.InnerText;
                allProductsList.Add(PackageName);
                XmlNodeList xmllistcategories = HostingOptions.DocumentElement.SelectNodes(string.Format("//Product[Title='{0}']/Categories/Category", PackageName));
                List<string> packageCategories = new List<string>();
                for (int ii = 0; ii < xmllistcategories.Count; ii++)
                {
                    string categoryEntry = xmllistcategories[ii].InnerText;
                    packageCategories.Add(categoryEntry);
                }

                for (int ii = 0; ii < packageCategories.Count; ii++)
                {
                    List<string> packageNames = new List<string>();
                    if (!categoryDictionary.ContainsKey(packageCategories[ii]))
                    {
                        packageNames.Add(PackageName);
                        categoryDictionary.Add(packageCategories[ii], packageNames);
                    }
                    else
                    {
                        categoryDictionary.TryGetValue(packageCategories[ii], out packageNames);
                        packageNames.Add(PackageName);
                        categoryDictionary.Remove(packageCategories[ii]);
                        categoryDictionary.Add(packageCategories[ii], packageNames);
                    }
                }
            }
            categoryDictionary.Add("all", allProductsList);
        }
    }


    #region Installation

    public class InstallationRequirements
    {
        public string URL;
        /// <summary>
        /// True if Install Requirements specified in HostingOptions.xml
        /// </summary>
        public bool InstallCheckRequired = false;
        /// <summary>
        /// True if the Package could be installed
        /// </summary>
        public bool InstallCheckSuccessfull = false;
        public bool ProductAlreadyInstalled = false;

        //is true when the InstallationRequirements could be successfully verified 
        //returns false when a problem (e.g. checkfile not found) occurred
        private bool problemOccurred = false;
        public bool isValidInstallationRequirements
        {
            get { return !problemOccurred; }
        }

        public List<string> CheckResults = new List<string>();
        public List<string> MissingRequirements = new List<string>();
        public DateTime LastCheckTime;

        string urlInstallationRequirements = string.Empty;
        XmlDocument docInstallationRequirements = new XmlDocument();
        List<XmlNode> InstallationNodes = new List<XmlNode>();

        public InstallationRequirements(XmlNode Product, XmlDocument XmlInstallationRequirements)
        {
            XmlDocument docProduct = new XmlDocument();
            this.docInstallationRequirements = XmlInstallationRequirements;

            //set timestamp
            this.LastCheckTime = DateTime.Now;

            docProduct.LoadXml(Product.OuterXml);

            string ProductName = Product.FirstChild.InnerText;
            string searchpath = string.Format("//Product[Title='{0}']/Requirements", ProductName);
            string searchpathInstallRequirements = string.Format("{0}/InstallRequirements/*", searchpath);
            string searchpathSelfCheck = string.Format("{0}/SelfCheck", searchpath);

            #region Check Installation
            XmlNodeList Requirements = docProduct.SelectNodes(searchpath);

            if (Requirements.Count > 0)
            {
                InstallCheckRequired = true;

                XmlNodeList SelfCheckRequirements = docProduct.SelectNodes(searchpathSelfCheck);

                if (SelfCheckRequirements.Count > 0)
                {
                    if (InstallationReferenceCheck_Success(SelfCheckRequirements))
                    {
                        try
                        {
                            this.ProductAlreadyInstalled = CheckInstallationRequirements();
                        }
                        catch
                        {
                            this.problemOccurred = true;
                        }

                        this.CheckResults.Clear();
                        if (this.ProductAlreadyInstalled == true)
                        {
                            this.CheckResults.Add("Product is already found and only one instance of this product can be installed.\nIn order to reinstall you would have to remove the product first.");
                        }
                        else
                        {
                            this.CheckResults.Add("Product is not yet found on the system.");
                        }
                    }
                    else
                    {
                        string ExMsg = string.Format("Invalid Installation Requirement found in {0} (SelfCheckRequirements).\r\nPlease check reference in {1}", Product.FirstChild.OwnerDocument.BaseURI, docInstallationRequirements.BaseURI);
                        this.CheckResults.Add(ExMsg);
                        problemOccurred = true;
                        return;
                    }

                }

                if (!this.ProductAlreadyInstalled)
                {
                    XmlNodeList InstallRequirements = docProduct.SelectNodes(searchpathInstallRequirements);

                    if (InstallRequirements.Count == 0)
                    {
                        this.InstallCheckSuccessfull = true;
                        this.CheckResults.Add("\nProduct does not depend on others.");
                        return;
                    }
                    if (InstallationReferenceCheck_Success(InstallRequirements))
                    {
                        this.CheckResults.Add("\nDependency-Check:\n-----");
                        try
                        {
                            this.InstallCheckSuccessfull = CheckInstallationRequirements();
                        }
                        catch
                        {
                            this.problemOccurred = true;
                        }
                    }
                    else
                    {
                        string ExMsg = string.Format("Invalid Installation Requirement found in {0} (InstallRequirements).\r\nPlease check reference in {1}", Product.FirstChild.OwnerDocument.BaseURI, docInstallationRequirements.BaseURI);
                        this.CheckResults.Add(ExMsg);
                        problemOccurred = true;
                        return;
                    }
                }
            #endregion
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        private bool CheckInstallationRequirements()
        {
            bool success = false;
            bool result = true;

            foreach (XmlNode Requirement in this.InstallationNodes)
            {
                string comment = string.Empty;
                string SpecialFolder = string.Empty;
                string SearchPath = string.Empty;
                string attribSearchPath = string.Empty;
                XmlNode specialFolderNode = null;
                string filename = string.Empty;



                string requirementPropertyName = Requirement.Attributes["Property"].Value;


                try
                {
                    #region take the result from cache if found and continue foreach loop
                    DateTimeCheckResult mydtcr;
                    if (HelperFunctions.CachedCheckInstallationRequirements.TryGetValue(requirementPropertyName, out mydtcr))
                    {
                        //take result from cache if check was done not longer than 1 minute ago - otherwhise delete cache entry and check again.
                        if (mydtcr.datetime > DateTime.Now.AddMinutes(-1))
                        {
                            success = mydtcr.result;
                            switch (success)
                            {
                                case true:
                                    this.CheckResults.Add(string.Format("ok: {0}", Requirement.Attributes["Property"].Value));
                                    result &= success;
                                    break;
                                case false:
                                    this.CheckResults.Add(string.Format("missing: {0}", Requirement.Attributes["Property"].Value));
                                    this.MissingRequirements.Add(Requirement.Attributes["Property"].Value);
                                    result &= success;
                                    break;
                            }
                            continue;
                        }
                        HelperFunctions.CachedCheckInstallationRequirements.Remove(requirementPropertyName);
                    }
                    #endregion

                    switch (Requirement.Name)
                    {

                        #region RegistryCheck
                        case "RegistryCheck":
                            object o = Registry.GetValue(Requirement.Attributes["Key"].Value, Requirement.Attributes["Value"].Value, null);
                            if (o == null)
                            {
                                success = false;
                                break;
                            }
                            switch (Requirement.FirstChild.Attributes["Method"].Value)
                            {
                                case "ValueGreaterThan":
                                    if (Convert.ToInt32(o) > Convert.ToInt32(Requirement.FirstChild.InnerText)) success = true;
                                    break;
                                case "ValueLessThan":
                                    if (Convert.ToInt32(o) < Convert.ToInt32(Requirement.FirstChild.InnerText)) success = true;
                                    break;
                                case "ValueEqualTo":
                                    if (o.ToString() == Requirement.FirstChild.InnerText) success = true;
                                    break;
                                case "ValueNotEqualTo":
                                    if (o.ToString() != Requirement.FirstChild.InnerText) success = true;
                                    break;
                                case "ValueExists":
                                    if (o.ToString() != string.Empty) success = true;
                                    break;
                                default:
                                    success = false;
                                    break;
                            }
                            break;
                        #endregion

                        #region FileCheck
                        case "FileCheck":
                            success = false;
                            SpecialFolder = string.Empty;
                            SearchPath = string.Empty;
                            filename = string.Empty;
                            attribSearchPath = string.Empty;
                            specialFolderNode = null;
                            attribSearchPath = Requirement.Attributes["SearchPath"].Value;
                            specialFolderNode = Requirement.Attributes.GetNamedItem("SpecialFolder");
                            if (specialFolderNode != null)
                            {
                                SpecialFolder = specialFolderNode.Value;
                                SearchPath = Path.GetFullPath(string.Format(@"{0}\{1}", HelperFunctions.ParseSpecialStrings(SpecialFolder), attribSearchPath));
                            }
                            else
                            {
                                SearchPath = Path.GetFullPath(string.Format(@"{0}", attribSearchPath));
                            }
                            filename = string.Format(@"{0}\{1}", SearchPath, Requirement.Attributes["FileName"].Value);
                            switch (Requirement.FirstChild.Attributes["Method"].Value)
                            {
                                case "ValueExists":
                                    if (File.Exists(filename)) success = true;
                                    break;

                                case "FileVersionGreaterThan":

                                    if (string.Compare(FileVersionInfo.GetVersionInfo(filename).FileVersion, Requirement.FirstChild.InnerText) > 0)
                                    {
                                        success = true;
                                    }
                                    break;

                                case "FileVersionEqualTo":
                                    if (string.Compare(FileVersionInfo.GetVersionInfo(filename).FileVersion, Requirement.FirstChild.InnerText) == 0)
                                    {
                                        success = true;
                                    }
                                    break;

                                case "FileVersionLessThan":

                                    if (string.Compare(FileVersionInfo.GetVersionInfo(filename).FileVersion, Requirement.FirstChild.InnerText) < 0)
                                    {
                                        success = true;
                                    }
                                    break;
                            }
                            break;
                        #endregion

                        #region ExternalCheck
                        case "ExternalCheck":
                            success = false;
                            SpecialFolder = string.Empty;
                            SearchPath = string.Empty;
                            filename = string.Empty;
                            attribSearchPath = string.Empty;
                            specialFolderNode = null;
                            attribSearchPath = Requirement.Attributes["SearchPath"].Value;
                            specialFolderNode = Requirement.Attributes.GetNamedItem("SpecialFolder");
                            if (specialFolderNode != null)
                            {
                                SpecialFolder = specialFolderNode.Value;
                                SearchPath = Path.GetFullPath(string.Format(@"{0}\{1}", HelperFunctions.ParseSpecialStrings(SpecialFolder), attribSearchPath));
                            }
                            else
                            {
                                SearchPath = Path.GetFullPath(string.Format(@"{0}", attribSearchPath));
                            }
                            filename = string.Format(@"{0}\{1}", SearchPath, Requirement.Attributes["CheckFile"].Value);
                            string arguments = string.Format("{0}", Requirement.Attributes["Arguments"].Value);
                            try
                            {
                                ProcessStartInfo startInfo = new ProcessStartInfo(filename);
                                startInfo.WorkingDirectory = SearchPath;
                                startInfo.Arguments = arguments;
                                startInfo.UseShellExecute = false;
                                startInfo.WindowStyle = ProcessWindowStyle.Hidden;
                                startInfo.CreateNoWindow = true;
                                startInfo.RedirectStandardError = true;
                                startInfo.RedirectStandardOutput = true;
                                Process CheckRequirementProcess = new Process();
                                CheckRequirementProcess.StartInfo = startInfo;
                                CheckRequirementProcess.Start();
                                //wait for maximal 10seconds for this check.
                                CheckRequirementProcess.WaitForExit(10000);
                                string output = CheckRequirementProcess.StandardOutput.ReadToEnd();
                                string compareMethod = Requirement.FirstChild.Attributes["Method"].Value;
                                string compareValue = Requirement.FirstChild.InnerText;
                                switch (compareMethod)
                                {
                                    case "ValueContains":
                                        if (output.Contains(compareValue)) success = true;
                                        break;
                                }
                            }
                            catch
                            {
                                success = false;
                            }
                            break;
                        #endregion

                        #region MsiProductCheck
                        case "MsiProductCheck":
                            Guid searchedGUID = new Guid(Requirement.Attributes["GUID"].Value);
                            success = this.SearchMsiProduct(searchedGUID);

                            break;
                        #endregion

                        default:
                            CheckResults.Add(string.Format("Check Time: {0}\nInvalid Installation Requirement: '{1}' found in: {2}!", this.LastCheckTime.ToLongTimeString(), Requirement.Name, Requirement.BaseURI));
                            success = false;
                            throw new InvalidInputException(string.Format("Check Time: {0}\nInvalid Installation Requirement: '{1}' found in: {2}!", this.LastCheckTime.ToLongTimeString(), Requirement.Name, Requirement.BaseURI));
                            break;
                    }
                }
                catch (SystemException ex)
                {
                    CheckResults.Add(string.Format("Check Time: {3}\nInvalid Installation Requirement '{0}' - {2} found in {1}!", Requirement.Name, Requirement.BaseURI, Requirement.Attributes["Value"].Value, this.LastCheckTime.ToLongTimeString()));
                    success = false;
                    throw new InvalidInputException(string.Format("Check Time: {3}\nInvalid Installation Requirement '{0}' - {2} found in {1}!", Requirement.Name, Requirement.BaseURI, Requirement.Attributes["Value"].Value, this.LastCheckTime.ToLongTimeString()));
                }

                switch (success)
                {
                    case true:
                        this.CheckResults.Add(string.Format("ok: {0}", Requirement.Attributes["Property"].Value));
                        result &= success;
                        HelperFunctions.CachedCheckInstallationRequirements.Add(requirementPropertyName, new DateTimeCheckResult(DateTime.Now, success));
                        break;
                    case false:
                        this.CheckResults.Add(string.Format("missing: {0}", Requirement.Attributes["Property"].Value));
                        this.MissingRequirements.Add(Requirement.Attributes["Property"].Value);
                        result &= success;
                        HelperFunctions.CachedCheckInstallationRequirements.Add(requirementPropertyName, new DateTimeCheckResult(DateTime.Now, success));
                        break;
                }
            }

            return result;
        }

        private bool SearchMsiProduct(Guid searchedGUID)
        {
            bool Success = false;

            //64 bit and 32bit OS differ in registry keys to hold product installations.
            //on 32Bit Apps register in HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall
            //on 64Bit (x64)Apps register in HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall and (x86) apps go to HKLM\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall

            RegistryKey[] rkArray;
            switch (HelperFunctions.OSBitness)
            {
                case "32":
                    rkArray = new RegistryKey[1];
                    rkArray[0] = Registry.LocalMachine.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Uninstall");
                    break;
                case "64":
                    rkArray = new RegistryKey[2];
                    rkArray[0] = Registry.LocalMachine.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Uninstall");
                    rkArray[1] = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall");
                    break;
                default:
                    rkArray = new RegistryKey[1];
                    rkArray[0] = Registry.LocalMachine.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Uninstall");
                    break;
            }
            foreach (RegistryKey rk in rkArray)
            {
                string[] subkeynames = rk.GetSubKeyNames();
                foreach (string subkey in subkeynames)
                {
                    Guid g = new Guid();
                    RegistryKey relevantkey = rk.OpenSubKey(subkey);
                    if (relevantkey.ValueCount > 0)
                    {
                        try
                        {
                            g = new Guid(subkey.ToString());
                        }
                        catch (FormatException)
                        {
                        }
                        if (g.CompareTo(searchedGUID) == 0)
                        {
                            Success = true;
                        }
                    }
                }
                if (Success == true) break;
            }

            return Success;
        }

        private bool InstallationReferenceCheck_Success(XmlNodeList InstallRequirements)
        {
            //loop through the installation requirements
            //every requirement for a product in hostingoptions must have a corresponding entry in installation requirements
            //if not return false
            bool referenceFound = false;
            bool result = true;

            this.InstallationNodes.Clear();

            foreach (XmlNode InstallRequirementsnode in InstallRequirements)
            {
                string RequirementName = InstallRequirementsnode.FirstChild.InnerText;
                XmlNodeList InstallRequirementProperties;
                try
                {
                    InstallRequirementProperties = docInstallationRequirements.DocumentElement.SelectNodes("//Requirements/*/@Property");
                }
                catch (Exception)
                {
                    return false;
                }
                referenceFound = false;
                foreach (XmlNode InstallRequirementPropertynode in InstallRequirementProperties)
                {
                    if (RequirementName == InstallRequirementPropertynode.InnerText)
                    {
                        referenceFound = true;
                        this.InstallationNodes.Add(InstallRequirementPropertynode.SelectSingleNode(".."));
                        break;
                    }
                }
                result &= referenceFound;
            }

            return result;
        }
    }



    #endregion

    #region UserInput
    public class UserInput
    {
        public bool Required = false;
        public List<UserInputParameter> Parameters = null;

        public UserInput(XmlNodeList Parameters)
        {
            this.Parameters = new List<UserInputParameter>();

            foreach (XmlNode input in Parameters)
            {
                //one userinput
                UserInputParameter prm = new UserInputParameter();
                this.Required = true;
                //prm.Name = input.SelectSingleNode("Input/Name").InnerText;
                //prm.InputType = input.SelectSingleNode("Input/Name").InnerText;

                string searchpath = string.Format("Input/Name");
                string searchpathType = string.Format("Input/Type");
                string searchpathDefault = string.Format("Input/Default");
                string searchpathDescription = string.Format("Input/Description");
                string searchpathRegex = string.Format("Input/RegularExpression");

                XmlNode xmlInputType = input.SelectSingleNode(searchpathType);
                XmlNode xmlInputDefault = input.SelectSingleNode(searchpathDefault);
                XmlNode xmlInputDescription = input.SelectSingleNode(searchpathDescription);
                XmlNode xmlInputRegularExpression = input.SelectSingleNode(searchpathRegex);


                if (xmlInputType == null) throw new InvalidInputException(string.Format("Invalid Input found!\r\nXmlPath='{0}'", searchpathType));
                if (xmlInputDefault == null) throw new InvalidInputException(string.Format("Invalid Input found!\r\nXmlPath='{0}')", searchpathType));
                if (xmlInputDescription == null) throw new InvalidInputException(string.Format("Invalid Input found!\r\nXmlPath='{0}')", searchpathType));


                prm.Name = input.SelectSingleNode(searchpath).InnerText;
                prm.Default = xmlInputDefault.InnerText;
                prm.Description = xmlInputDescription.InnerText;

                switch (xmlInputType.InnerText.ToLower())
                {
                    case "bool":
                        prm.InputType = UserInputType.uiTypeBool;
                        break;
                    case "string":
                        prm.InputType = UserInputType.uiTypeString;
                        if (xmlInputRegularExpression != null) prm.RegularExpression = xmlInputRegularExpression.InnerText;
                        break;
                    case "passwordstring": //renders differently (shows ****** instead of cleartext) in UserInputDialog
                        prm.InputType = UserInputType.uiTypePWDString;
                        if (xmlInputRegularExpression != null) prm.RegularExpression = xmlInputRegularExpression.InnerText;
                        break;
                    default:
                        break;
                }

                prm.ParameterXmlNode = input;
                this.Parameters.Add(prm);
            }

        }
    }




    public struct UserInputParameter
    {
        public string Name;
        public UserInputType InputType;
        public string Description;
        public string Default;
        public string RegularExpression;
        public object Result;
        public XmlNode ParameterXmlNode;
    }

    public enum UserInputType
    {
        uiTypeBool,
        uiTypeString,
        uiTypePWDString,
    }


    #endregion

    #region global

    [global::System.Serializable]
    public class InvalidInputException : Exception
    {

        public InvalidInputException() { }
        public InvalidInputException(string message) : base(message) { }
        public InvalidInputException(string message, Exception inner) : base(message, inner) { }
        protected InvalidInputException(
          System.Runtime.Serialization.SerializationInfo info,
          System.Runtime.Serialization.StreamingContext context)
            : base(info, context) { }
    }

    #endregion

    #region HelperFunctions
    public class HelperFunctions
    {

        //property to function as a cache for the requirements checks.
        static Dictionary<string, DateTimeCheckResult> dict = new Dictionary<string, DateTimeCheckResult>();
        public static Dictionary<string, DateTimeCheckResult> CachedCheckInstallationRequirements
        {
            get { return dict; }
        }

        /// <summary>
        /// Parses the input string for a something like %temp% and replaces it with a corresponding value (if found) otherwhise return same input string
        /// </summary>
        /// <param name="input">The input string to </param>
        /// <returns>Returns the parsed string</returns>
        public static string ParseSpecialStrings(string input)
        {
            if (!input.Contains("%")) return input;

            Regex regex = new Regex("%[\\w]*%");

            try
            {
                MatchCollection mcol = regex.Matches(input);
                if (mcol.Count > 0)
                {
                    foreach (Match m in mcol)
                    {
                        switch (m.Value.ToUpper())
                        {
                            case "%INSTALLPATH%":
                                //string filePath = this.Package.InstallationUrl;
                                string filePath = System.Windows.Forms.Application.LocalUserAppDataPath + "\\downloads";
                                input = input.Replace(m.Value, filePath);
                                break;
                            case "%TEMPSUBDIRECTORY%":
                                //string tempdir = Environment.CurrentDirectory + "\\" + ConfigurationSettings.AppSettings["TempSubDirectory"];
                                string tempdir = System.Windows.Forms.Application.LocalUserAppDataPath + "\\temp";   //default(vista) C:\Users\bfrank\AppData\Local\raktajino\raktajino-packagemanager\1.0.0.0\temp
                                if (!Directory.Exists(tempdir)) Directory.CreateDirectory(tempdir);
                                input = input.Replace(m.Value, tempdir);
                                break;
                            case "%TMP%":    //default(vista) C:\Users\bfrank\AppData\Local\Temp
                                input = input.Replace(m.Value, Environment.GetEnvironmentVariable("TMP"));
                                break;
                            case "%TEMP%":    //default(vista) C:\Users\bfrank\AppData\Local\Temp
                                input = input.Replace(m.Value, Environment.GetEnvironmentVariable("TMP"));
                                break;
                            case "%INSTALLLOGS%":    //default path to write the installation logs to
                                //string folder = System.Windows.Forms.Application.LocalUserAppDataPath + "\\logs";
                                string folder = System.Windows.Forms.Application.CommonAppDataPath + "\\installlogs";
                                if (!Directory.Exists(folder)) Directory.CreateDirectory(folder);
                                input = input.Replace(m.Value, folder);
                                break;
                            case "%SYSTEMDRIVE%":     //default c:
                                input = input.Replace(m.Value, Environment.GetEnvironmentVariable("SYSTEMDRIVE"));
                                break;
                            case "%SYSTEMROOT%":      //default c:\windows
                                input = input.Replace(m.Value, Environment.GetEnvironmentVariable("SystemRoot"));
                                break;
                            case "%SYSTEM32%":        //default c:\windows\system32
                                input = input.Replace(m.Value, Environment.GetFolderPath(Environment.SpecialFolder.System));
                                break;
                            case "%WINDIR%":     //default c:\windows
                                input = input.Replace(m.Value, Environment.GetEnvironmentVariable("WINDIR"));
                                break;
                            case "%PROGRAMFILES%":    //default c:\program files
                                input = input.Replace(m.Value, Environment.GetEnvironmentVariable("PROGRAMFILES"));
                                break;
                            case "%USERPROFILE%":    //default C:\Users\bfrank
                                input = input.Replace(m.Value, Environment.GetEnvironmentVariable("USERPROFILE"));
                                break;
                            case "%TIMESTAMP%":     //e.g. "20080624T13207" (= 24.06.2008 13:20:07)
                                string time = string.Format("{0:0000}{1:00}{2:00}T{3}{4}{5}", DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, DateTime.Now.Hour, DateTime.Now.Minute, DateTime.Now.Second);
                                input = input.Replace(m.Value, time);
                                break;
                            case "%OS%":     //e.g. "6.0.6001.65536" for vista SP1
                                string os = Environment.OSVersion.Version.ToString();
                                input = input.Replace(m.Value, os);
                                break;
                            case "%OSNAME%":     //e.g. "Microsoft+Windows+Web+Server+2008" 
                                try
                                {
                                    ManagementObjectSearcher ObjSearcher;
                                    ObjSearcher = new ManagementObjectSearcher("SELECT Caption FROM Win32_OperatingSystem");
                                    string osName = string.Empty;
                                    foreach (ManagementObject MgmtObj in ObjSearcher.Get())
                                    {
                                        osName += MgmtObj["Caption"].ToString() + " ";
                                    }
                                    Regex reg = new Regex("[^a-z0-9A-Z\x20\x28\x29]"); //replaces everything except a-z, A-Z, 0-9, ( ), and SPACE
                                    osName = reg.Replace(osName, "");
                                    osName = osName.TrimEnd(new char[] { ' ' });
                                    input = input.Replace(m.Value, HttpUtility.UrlEncode(osName));
                                }
                                catch (Exception)
                                {
                                    input = input.Replace(m.Value, "unknown");
                                }
                                break;
                            case "%ADDRESSWIDTH%":  //e.g. determines the bitness of the OS possible values "32" or "64" or "unknown"
                                try
                                {
                                    ManagementObjectSearcher ObjSearcher;
                                    ObjSearcher = new ManagementObjectSearcher("SELECT AddressWidth FROM Win32_Processor");
                                    string addressWidth = string.Empty;
                                    foreach (ManagementObject MgmtObj in ObjSearcher.Get())
                                    {
                                        addressWidth += MgmtObj["AddressWidth"].ToString() + " ";
                                    }
                                    Regex reg = new Regex("[0-9]{2}");
                                    addressWidth = reg.Match(addressWidth).ToString();
                                    addressWidth = addressWidth.TrimEnd(new char[] { ' ' });
                                    input = input.Replace(m.Value, HttpUtility.UrlEncode(addressWidth));
                                }
                                catch (Exception)
                                {
                                    input = input.Replace(m.Value, "unknown");
                                }
                                break;
                            case "%LANGUAGE%":     //default c:\windows
                                string language = System.Globalization.CultureInfo.InstalledUICulture.Name;
                                input = input.Replace(m.Value, language);
                                break;
                            case "%CULTURE%":     //default "en-US"
                                string culture = ConfigurationSettings.AppSettings["Culture"];
                                input = input.Replace(m.Value, culture);
                                break;
                        }
                    }
                }
            }
            catch (ArgumentNullException)
            {
                return string.Empty;
            }

            return input;
        }

        static string osBitness = string.Empty;
        /// <summary>
        /// Returns a string representing the OS bitness, possible values "32", "64", "unknown".
        /// </summary>
        public static string OSBitness
        {
            get
            {
                if (osBitness == string.Empty)
                {
                    osBitness = HelperFunctions.ParseSpecialStrings("%ADDRESSWIDTH%");
                }
                return osBitness;
            }
        }
    }
    public struct DateTimeCheckResult
    {
        public DateTime datetime;
        public bool result;
        public DateTimeCheckResult(DateTime dt, bool b)
        {
            datetime = dt;
            result = b;
        }
    };
    #endregion
}
